
#include <dev.h>
#include <mem.h>
#include "pic.h"
#include "rtc.h"
#include "sio.h"
#include "pci.h"
#include "i2c.h"
#include "misc.h"

/* ------------------------------------------------------------------------- */

static bool mcpx_map;

/* ------------------------------------------------------------------------- */

void fastcall dev_init(void)
{
	mcpx_map = true;

	pic_init();
	rtc_init();
	sio_init();
	pci_init();
	i2c_init();
	misc_init();
}

void fastcall set_mcpx_map(bool fix)
{
	mcpx_map = fix;
}

uint32 fastcall io_read(uint16 port, size_t size)
{
	size_t len;
	uint32 mask, data, offset, tmp;
	uint8 *ptr;

	mask = size_mask(size);
	data = 0xffffffff;
	ptr = (uint8 *)&data;

	for (; size; size -= len, port += (uint16)len, ptr += len) {

		switch (port) {
		case PIC_MASTER_0:
		case PIC_MASTER_1:
		case PIC_SLAVE_0:
		case PIC_SLAVE_1:
			*ptr = pic_get_data(port & 0x0080, port & 0x0001);
			len = 1;
			continue;
		case PIC_MASTER_ELCR:
		case PIC_SLAVE_ELCR:
			*ptr = pic_get_elcr(port & 0x0001);
			len = 1;
			continue;
		case SIO_INDEX:
			*ptr = sio_get_index();
			len = 1;
			continue;
		case SIO_DATA:
			*ptr = sio_get_data();
			len = 1;
			continue;
		case PIT_CHANNEL_0:
		case PIT_CHANNEL_1:
		case PIT_CHANNEL_2:
		case PIT_COMMAND:
			write_log("[PIT] inb %02x", port);
			*ptr = 0x00;
			len = 1;
			continue;
		case PPI_PORT_A:
		case PPI_PORT_B:
		case PPI_PORT_C:
		case PPI_CON:
			write_log("[PPI] inb %02X", port);
			*ptr = 0x00;
			len = 1;
			continue;
		case RTC_INDEX:
			assert(0);
			len = 1;
			continue;
		case RTC_TARGET:
			*ptr = rtc_read();
			len = 1;
			continue;
		default:
			break;
		}

		if (port >= PCI_CONFIG_ADDR && port < PCI_CONFIG_ADDR + PCI_CONFIG_ADDR_SIZE) {
			offset = port - PCI_CONFIG_ADDR;
			len = min(size, PCI_CONFIG_ADDR_SIZE - offset);
			tmp = pci_get_config_addr() >> (offset << 3);
			memcpy(ptr, &tmp, len);
			continue;
		}

		if (port >= PCI_CONFIG_DATA && port < PCI_CONFIG_DATA + PCI_CONFIG_DATA_SIZE) {
			offset = port - PCI_CONFIG_DATA;
			len = min(size, PCI_CONFIG_DATA_SIZE - offset);
			tmp = pci_get_config_data() >> (offset << 3);
			memcpy(ptr, &tmp, len);
			continue;
		}

		len = pci_io_mapping_read(port, ptr, size);
		if (!len)
			break;
	}

	return data & mask;
}

void fastcall io_write(uint16 port, uint32 data, size_t size)
{
	size_t len;
	uint32 offset, tmp;
	const uint8 *ptr;

	data &= size_mask(size);
	ptr = (const uint8 *)&data;

	for (; size; size -= len, port += (uint16)len, ptr += len) {

		switch (port) {
		case PIC_MASTER_0:
		case PIC_MASTER_1:
		case PIC_SLAVE_0:
		case PIC_SLAVE_1:
			pic_set_data(port & 0x0080, port & 0x0001, *ptr);
			len = 1;
			continue;
		case PIC_MASTER_ELCR:
		case PIC_SLAVE_ELCR:
			pic_set_elcr(port & 0x0001, *ptr);
			len = 1;
			continue;
		case SIO_INDEX:
			sio_set_index(*ptr);
			len = 1;
			continue;
		case SIO_DATA:
			sio_set_data(*ptr);
			len = 1;
			continue;
		case PIT_CHANNEL_0:
		case PIT_CHANNEL_1:
		case PIT_CHANNEL_2:
		case PIT_COMMAND:
			write_log("[PIT] outb %02X, %02X", port, *ptr);
			len = 1;
			continue;
		case PPI_PORT_A:
		case PPI_PORT_B:
		case PPI_PORT_C:
		case PPI_CON:
			write_log("[PPI] outb %02X, %02X", port, *ptr);
			len = 1;
			continue;
		case RTC_INDEX:
			rtc_index(*ptr);
			len = 1;
			continue;
		case RTC_TARGET:
			rtc_write(*ptr);
			len = 1;
			continue;
		default:
			break;
		}

		if (port >= PCI_CONFIG_ADDR && port < PCI_CONFIG_ADDR + PCI_CONFIG_ADDR_SIZE) {
			offset = port - PCI_CONFIG_ADDR;
			len = min(size, PCI_CONFIG_ADDR_SIZE - offset);
			memcpy(&tmp, ptr, len);
			pci_set_config_addr(tmp << (offset << 3), make_mask(offset, len));
			continue;
		}

		if (port >= PCI_CONFIG_DATA && port < PCI_CONFIG_DATA + PCI_CONFIG_DATA_SIZE) {
			offset = port - PCI_CONFIG_DATA;
			len = min(size, PCI_CONFIG_DATA_SIZE - offset);
			memcpy(&tmp, ptr, len);
			pci_set_config_data(tmp << (offset << 3), make_mask(offset, len));
			continue;
		}

		len = pci_io_mapping_write(port, &data, size);
		if (!len)
			break;
	}
}

uint32 fastcall memory_read(uint32 addr, size_t size)
{
	uint32 data = 0xffffffff;

	if (mcpx_map && addr >= MCPX_BASE && addr <= MCPX_TOP)
		return mcpx_read(addr - MCPX_BASE) & size_mask(size);

	if (addr >= RAM_BASE && addr <= RAM_TOP)
		return ram_read(addr - RAM_BASE) & size_mask(size);

	if (addr >= ROM_BASE && addr <= ROM_TOP)
		return rom_read(addr - ROM_BASE) & size_mask(size);

	pci_mem_mapping_read(addr, &data, size);

	return data;
}

void fastcall memory_write(uint32 addr, uint32 data, size_t size)
{
	uint32 offset;

	data &= size_mask(size);

	if (addr >= RAM_BASE && addr <= RAM_TOP) {
		offset = addr - RAM_BASE;
		data |= ram_read(offset) & ~size_mask(size);
		ram_write(offset, data);
		return;
	}

	/* ROM is read-only */
	if (addr >= ROM_BASE && addr <= ROM_TOP)
		return;

	pci_mem_mapping_write(addr, &data, size);
}

/* ------------------------------------------------------------------------- */
